main(List<String> args) {
  // 构造方法
  var person = new Person('Tom', 20, 'Ch');

  person.work();

  var person1 = new Person.withName('Bob');
  person1.work();

  const person2 = const PersonConst('Anna', 18, 'TW');
  person2.work();

  var page = Page();
  page.scrollUp();

  Page.scrollDown();

  // 如果前面值为空，则不向后执行。不为空则向后执行
  PersonO personO;
  personO?.work();
  PersonO personO1 = new PersonO();
  personO1?.work();

  var personO2;
  personO2 = '';
  // personO2 = new PersonO();
  // (personO2 as PersonO).work();
  // personO2.work();

  if (personO2 is PersonO) {
    personO2.work();
  }
  if (personO2 is! PersonO) {
    print('personO2 is not PersonO');
  }

  var p = new PersonO();
  // p.name = 'Tom';
  // p.age = 20;
  p
    ..name = 'Tom'
    ..age = 20
    ..work();

  new PersonO()
    ..name = 'Tom'
    ..age = 20
    ..work();

  var p1 = PersonCall();
  var res = p1('Bob', 30);
  print(res);
}

class Person {
  String name;
  int age;
  final String address;

  // 构造函数
  // Person(String name, int age) {
  //   this.name = name;
  //   this.age = age;
  // }

  // 简写语法糖
  // Person(this.name, this.age);
  // Person(this.name, this.age) {
  //   print(name);
  // }

  // 构造函数的这种写法不能初始化final属性
  // Person(String name, int age, String address) {
  //   this.name = name;
  //   this.age = age;
  //   this.address = address;
  // }
  // 简写语法糖可以初始化final属性，前提是final属性并没有初始化
  Person(this.name, this.age, this.address);
  // 简写语法糖，初始化final属性实在构造方法之前执行的

  // Person() {}

  // 构造函数重载
  Person.withName(String name, {this.address = 'CH'}) {
    this.name = name;
  }
  Person.withAge(this.age, this.address);

  void work() {
    print('Name is $name, Age is $age, He is working...');
  }
}

/**
 * 如果没有自定义构造方法，则会有默认的构造方法
 * 如果存在自定义构造方法，则默认构造方法无效
 * 构造方法不能重载
 */

/**
 * 命名构造方法，可以实现多个构造方法
 * 使用 类名.方法 的形式实现
 */

/**
 * 如果类是不可变状态，可以把对象定义成编译时常量
 * 使用 const 声明构造方法，并且所有属性都为 final
 * 使用 const 声明对象，可以省略
 */
class PersonConst {
  final String name;
  final int age;
  final String gender;

  // 常量构造方法
  const PersonConst(this.name, this.age, this.gender);

  work() {
    print('work....$name,$age,$gender');
  }
}

/**
 * 工厂构造方法类似于设计模式中的工厂模式
 * 在构造函数前添加关键字 factory 实现一个工厂构造方法
 * 在工厂构造方法中可以返回对象
 */
class Logger {
  final String name;

  static final Map<String, Logger> _cache = <String, Logger>{};

  // 下边是两个构造方法 重载
  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    }

    final logger = Logger._internal(name);
    _cache[name] = logger;
    return logger;
  }

  Logger._internal(this.name);

  // 普通构造方法不能有返回值，工厂构造方法可以有返回值
  // Logger._internal1(this.name) {
  //   return null;
  // }

  void log(String msg) {
    print(msg);
  }
}

/**
 * 初始化列表会在构造方法之前执行（和简写构造方法语法糖一样）
 * 使用逗号分隔初始化列表
 * 初始化列表常用于对final属性变量初始化值
 */
class PersonFinal {
  String name;
  int age;

  final String gender;

  PersonFinal(this.name, this.age, this.gender);

  // 这种写法就是初始化列表 会在构造方法之前执行
  PersonFinal.WithMap(Map map)
      : name = map['name'],
        gender = map['gender'] {
    this.age = map['age'];
  }

  work() {
    print('PersonFinal--------');
  }
}

/**
 * 静态成员
 * 
 * 使用 static 关键词来实现类级别的变量和函数（不是对象级别的）
 * 静态成员不能访问非静态成员，非静态成员可以访问静态成员
 * 类中的常量需要使用 static const 声明
 */
class Page {
  static const maxPage = 10;

  static int currentPage = 1;

  static scrollDown() {
    currentPage = 1;
    print('ScrollDown...');
  }

  scrollUp() {
    currentPage++;
    print('ScrollUp...');
  }
}

/**
 * 对象操作符
 * 
 * 条件成员的访问：?.
 * 类型转换：as
 * 是否指定类型：is, is!
 * 级联操作符：..
 */
class PersonO {
  String name;
  int age;

  work() {
    print('PersonO-------$name,$age');
  }
}

/**
 * 对象 call 方法
 * 如果类实现了 call() 方法，则该类的对象可以作为方法使用
 */
class PersonCall {
  String name;
  int age;

  work() {
    print('name=$name,age=$age');
  }

  call(String name, int age) {
    print('call PersonCall---name=$name,age=$age');
    return {'name': name, 'age': age};
  }
}
